using DemoContentLoader;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace DemoContentBuilder
{
    public class FontPacker
    {
        int atlasWidth;
        int alignmentMask;
        int padding;
        int paddingx2;

        int start;
        int rowIndex;
        public int Height { get; private set; }

        struct Interval
        {
            public int Start;
            public int End; // 在技术上是多余的,但它稍微简化了实现。表现并不重要。
            public int Height;

            public override string ToString()
            {
                return $"[{Start}, {End}): {Height}";
            }
        }
        List<Interval> intervals;

        public FontPacker(int width, int mipLevels, int padding, int characterCount)
        {
            this.atlasWidth = width;
            this.alignmentMask = (1 << mipLevels) - 1;
            this.padding = padding;
            this.paddingx2 = padding * 2;

            intervals = new List<Interval>(characterCount);
            intervals.Add(new Interval { Start = 0, End = atlasWidth, Height = 0 });
        }


        int AddAndGetBaseHeight(int queryStart, int queryEnd, int newGlyphHeight)
        {
            Debug.Assert(queryStart >= 0 && queryStart < atlasWidth && queryEnd > 0 && queryEnd <= atlasWidth);
            // 性能在这里并不重要。只需扫描设置的间隔,直到第一个与查询间隔完全重叠的间隔。
            int firstOverlappedIndex = intervals.Count;
            int baseHeight = 0;
            for (int i = 0; i < intervals.Count; ++i)
            {
                var interval = intervals[i];
                if (interval.End > queryStart)
                {
                    firstOverlappedIndex = i;
                    baseHeight = interval.Height;
                    break;
                }
            }
            // 现在扫描,直到之后不重叠的第一个间隔。
            int lastOverlappedIndex = firstOverlappedIndex;
            for (int i = firstOverlappedIndex + 1; i < intervals.Count; ++i)
            {
                var interval = intervals[i];
                if (interval.Start < queryEnd)
                {
                    if (interval.Height > baseHeight)
                        baseHeight = interval.Height;
                    lastOverlappedIndex = i;
                }
                else
                    break;
            }
            // 对齐并四舍五入基准高度。
            baseHeight = (baseHeight + alignmentMask) & (~alignmentMask);

            var firstInterval = intervals[firstOverlappedIndex];
            Interval queryInterval;
            queryInterval.Start = queryStart;
            queryInterval.End = queryEnd;
            queryInterval.Height = baseHeight + newGlyphHeight;
            if (queryInterval.Height > Height)
                Height = queryInterval.Height;
            Debug.Assert(queryInterval.End > queryInterval.Start);

            if (firstOverlappedIndex == lastOverlappedIndex && firstInterval.Start <= queryStart && firstInterval.End >= queryEnd)
            {
                if (firstInterval.Start == queryStart && firstInterval.End == queryEnd)
                {
                    // 完美的替代者。
                    intervals[firstOverlappedIndex] = queryInterval;
                }
                else if (firstInterval.Start == queryStart)
                {
                    // 新间隔应插入在第一个间隔之前。修改第一个间隔。
                    firstInterval.Start = queryEnd;
                    Debug.Assert(firstInterval.End > firstInterval.Start);
                    intervals[firstOverlappedIndex] = firstInterval;
                    intervals.Insert(firstOverlappedIndex, queryInterval);
                }
                else if (firstInterval.End == queryEnd)
                {
                    // 应在第一个间隔之后插入新间隔。修改第一个间隔。
                    firstInterval.End = queryStart;
                    Debug.Assert(firstInterval.End > firstInterval.Start);
                    intervals[firstOverlappedIndex] = firstInterval;
                    intervals.Insert(firstOverlappedIndex + 1, queryInterval);
                }
                else
                {
                    // 查询间隔在间隔内,两端都有可用的空间。
                    // 再添加两个间隔-查询间隔和另一侧的间隔。
                    // 我们将现有的间隔视为左侧。
                    var otherSideInterval = firstInterval;
                    otherSideInterval.Start = queryEnd;
                    firstInterval.End = queryStart;
                    Debug.Assert(firstInterval.End > firstInterval.Start);
                    intervals[firstOverlappedIndex] = firstInterval;

                    intervals.Insert(firstOverlappedIndex + 1, queryInterval);
                    Debug.Assert(otherSideInterval.End > otherSideInterval.Start);
                    intervals.Insert(firstOverlappedIndex + 2, otherSideInterval);
                }
            }
            else
            {
                // 查询覆盖多个时间间隔。
                int removalStartIndex;
                if (firstInterval.Start == queryStart)
                {
                    // 第一个重叠索引包含在查询间隔中。它应该被移除。
                    removalStartIndex = firstOverlappedIndex;
                }
                else
                {
                    // 第一个重叠索引不包含;请修改其结尾以匹配查询开始。
                    removalStartIndex = firstOverlappedIndex + 1;
                    firstInterval.End = queryStart;
                    Debug.Assert(firstInterval.End > firstInterval.Start);
                    intervals[firstOverlappedIndex] = firstInterval;
                }

                var lastInterval = intervals[lastOverlappedIndex];
                int removalEndIndex;
                if (lastInterval.End == queryEnd)
                {
                    // 最后一个重叠间隔包含在查询间隔中。它应该被移除。
                    removalEndIndex = lastOverlappedIndex;
                }
                else
                {
                    // 不包含最后一个重叠间隔;请修改其开始以匹配查询结束。
                    removalEndIndex = lastOverlappedIndex - 1;
                    lastInterval.Start = queryEnd;
                    Debug.Assert(lastInterval.End > lastInterval.Start);
                    intervals[lastOverlappedIndex] = lastInterval;
                }
                // 请注意,End是一个包含边界。所包含间隔的总数为removalEndIndex-removalStartIndex+1,
                // 但是重用其中一个可以避免不必要的插入。
                var removedCount = removalEndIndex - removalStartIndex;
                if (removedCount >= 0)
                {
                    intervals.RemoveRange(removalStartIndex, removalEndIndex - removalStartIndex);
                    intervals[removalStartIndex] = queryInterval;
                }
                else
                {
                    intervals.Insert(removalStartIndex, queryInterval);
                }
            }
            return baseHeight;
        }

        private void FillCharacterMinimum(ref CharacterData characterData, int end)
        {
            characterData.SourceMinimum.X = padding + start;
            characterData.SourceMinimum.Y = padding + AddAndGetBaseHeight(start, end, (int)characterData.SourceSpan.Y + paddingx2);
        }


        public void Add(ref CharacterData characterData)
        {
            int allocationWidth = (int)(paddingx2 + characterData.SourceSpan.X);
            if (allocationWidth > atlasWidth)
            {
                throw new ArgumentException(
                    "A single character that's wider than the entire atlas isn't gonna work. Is the FontPacker incorrectly initialized? Is the rasterized font size ridiculously huge?");
            }
            if ((rowIndex & 1) == 0)
            {
                // 从左到右放置字形。
                start = (start + alignmentMask) & (~alignmentMask);
                var end = start + allocationWidth;

                if (end <= atlasWidth)
                {
                    FillCharacterMinimum(ref characterData, end);
                    start = end;
                }
                else
                {
                    // 此字形不适合;需要移到下一行。
                    ++rowIndex;
                    start = atlasWidth;
                    Add(ref characterData);
                }
            }
            else
            {
                // 从右到左放置字形。
                start -= allocationWidth;
                if (start >= 0)
                {
                    // 延迟对齐;对齐不会使其变为负数。
                    start = start & (~alignmentMask);
                    var end = start + allocationWidth;
                    FillCharacterMinimum(ref characterData, end);
                }
                else
                {
                    // 此字形不适合;需要移到下一行。
                    ++rowIndex;
                    start = 0;
                    Add(ref characterData);
                }

            }
        }

    }
}
